package io.gitee.chearnee.fw.datasecurity.core.advice;

import io.gitee.chearnee.fw.datasecurity.core.annotation.control.DisableSD;
import io.gitee.chearnee.fw.datasecurity.core.processor.SDResponseBodyProcessor;
import io.gitee.chearnee.fw.datasecurity.core.properties.DataSecurityDesensitizationProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * @author cn
 * @version 1.0
 * @description
 * @date 11/9/2022 4:30 PM
 */
@Slf4j
@Order(-10)
public class SDResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Autowired
    private DataSecurityDesensitizationProperty desensitizationProperty;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        if (!desensitizationProperty.isEnabled()) {
            return false;
        }
        Set<Class<?>> disabledSD = getDisabledSD(returnType);
        if (disabledSD == null) {
            return true;
        }
        return !disabledSD.isEmpty();
    }

    private Set<Class<?>> getDisabledSD(MethodParameter returnType) {
        Set<Class<?>> classSet = null;
        DisableSD disableSD = returnType.getDeclaringClass().getAnnotation(
                DisableSD.class);
        if (disableSD != null) {
            classSet = new HashSet<>(Arrays.asList(disableSD.value()));
        }
        disableSD = returnType.getMethodAnnotation(DisableSD.class);
        if (disableSD != null) {
            if (classSet == null) {
                classSet = new HashSet<>();
            }
            classSet.addAll(Arrays.asList(disableSD.value()));
        }
        return classSet;
    }

    private Set<Class<?>> getDisabledClasses(MethodParameter returnType) {
        Set<Class<?>> disabledSD = getDisabledSD(returnType);
        if (disabledSD == null) {
            return new HashSet<>();
        }
        return disabledSD;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        Set<Class<?>> disabledClasses = getDisabledClasses(returnType);
        return SDResponseBodyProcessor.execute(body, returnType, disabledClasses);
    }
}
